package messageprocessors;

import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import util.DSPropertyManager;

import messages.DSMessage;
import messages.DSMessageType;

public class Server extends MessageProcessor {

	private static Logger log = Logger.getLogger(Server.class);

	// read xmpp id and password from properties file
	private static String networkid = DSPropertyManager.getInstance()
			.getStringValue("javaserverid", "javaserver");
	private static String networkpw = DSPropertyManager.getInstance()
			.getStringValue("javaserverpassword", "javaserver");

	public Server() {
		this(networkid, networkpw);
	}

	public Server(String id, String xmppPassword) {
		super(id, xmppPassword);
		availableClients = new HashMap<String, Boolean>();
	}

	/**
	 * Mapping from jabberids to availability
	 */
	HashMap<String, Boolean> availableClients;

	public void distributeMessageToAvailableClients(DSMessage message)
			throws Exception {
		if (this.availableClients == null || this.availableClients.size() < 1) {
			log.warn("JAVASERVER: No Clients are available!");
			throw new Exception("No Clients are available!");
		}
		log.debug("JAVASERVER: Sending message: " + message
				+ "; to a all available clients!");
		for (String curClientId : this.availableClients.keySet()) {
			if (this.availableClients.get(curClientId)) {
				log.debug("JAVASERVER: Sending message to clientid: " + curClientId);
				this.sendMessage(message, curClientId);
			}
		}
	}

	public void distributeMessageToGroup(String groupAdress, DSMessage message)
			throws Exception {

	}

	/**
	 * sends a status server status message to the given targetid
	 * 
	 * @param targetid
	 */
	private void sendServerStatusToTargetId(String targetid) {
		Map<String, String> propMap = new HashMap<String, String>();
		String availableClientsKey = "availableClients";

		// generate available clients string
		String availableClients = "";
		if (this.availableClients == null || this.availableClients.size() < 1) {
			propMap.put(availableClientsKey, "none");
		}
		for (String curClientid : this.availableClients.keySet()) {
			String curStr = curClientid + ":";
			curStr += this.availableClients.get(curClientid);
			availableClients += curStr;
			availableClients += "\n";
		}

		// fill property map
		propMap.put(availableClientsKey, availableClients);
		propMap.put("status", "" + this.isStatus());

		if (this.getLastMessageReceived() != null) {
			propMap.put("lastMessageReceived", this.getLastMessageReceived()
					.toString());
		} else {
			propMap.put("lastMessageReceived", "null");
		}

		if (this.getLastMessageSent() != null) {
			propMap.put("lastMessageSent", this.getLastMessageSent().toString());
		} else {
			propMap.put("lastMessageSent", "null");
		}

		// initialize and send message to given targetid
		DSMessage msg = new DSMessage(propMap);
		msg.setType(DSMessageType.STATUS);
		msg.setOriginId(this.getId());
		msg.setTargetId(targetid);

		try {
			this.sendMessage(msg, targetid);
		} catch (Exception e) {
			log.debug("JAVASERVER: An error occured while sending a status message to id:"
					+ targetid);
			e.printStackTrace();
		}
		
		this.setLastMessageSent(msg);
	}

	@Override
	public void processMessage(DSMessage message) throws Exception {

		if (message == null) {
			log.debug("JAVASERVER: Retrieved null message!");
			return;
		}

		// read message contents
		if (message.getType() == null) {
			throw new Exception("The message doesn't contain a DSMessageType!");
		}

		this.setLastMessageReceived(message);

		//log.debug("Server retrieved: " + message);

		// add sender to available clients if not already present in map
		if (!availableClients.keySet().contains(message.getOriginId())) {
			availableClients.put(message.getOriginId(), true);
			setChanged();
			notifyObservers(availableClients);
		}

		// --------------message processing
		// ------------------------------------------------
		String targetid = message.getTargetId();
		String originid = message.getOriginId();
		
		if (targetid == null) {
			throw new Exception(
					"The DSMessage must have a targetid declared!");
		}
		
		if (originid == null) {
			throw new Exception(
					"The DSMessage must have a originid declared!");
		}
		
		// process command message
		if (message.getType() == DSMessageType.COMMAND) {
			
			// check if the message has the javaserver as target
			if (targetid.equals(this.getId())) {
				//the server doesn't process command messages currently
				log.debug("JAVASERVER: The Javaserver retreived a command message with target=javaserver, nothing implemented here!");
			} 
			//redirect if the javaserver is not the target
			else {

				// log message
				log.debug("JAVASERVER: COMMAND-Message retrieved: " + message);

				// push to target
				log.debug("JAVASERVER: Redirecting COMMAND-message to id: " + targetid);
				this.sendMessage(message, message.getTargetId());
				this.setLastMessageSent(message);
			}
		}

		// process status message---------------------------------------
		else if (message.getType() == DSMessageType.STATUS) {
			//is the server the recipient?
			if(targetid.equals(this.getId())) {
				log.debug("JAVASERVER: The server retrieved a status message from: " + originid + ", with message: " + message);
			}
			// redirect further
			else {
				log.debug("JAVASERVER: Redirecting STATUS-message to id:" + targetid);
				//push to target
				this.sendMessage(message, targetid);
				this.setLastMessageSent(message);
			}
		}
		// process error message---------------------------------------
		else if (message.getType() == DSMessageType.ERROR) {
			log.debug("JAVASERVER: An error message was retrieved: " + message);
		}
		// process statusrequest----------------------------------------
		else if (message.getType() == DSMessageType.STATUSREQUEST) {
			// log message
			log.debug("JAVASERVER: STATUSREQUEST-Message retrieved: " + message);

			if (targetid.equals(this.getId())) {
				log.debug("JAVASERVER: Reporting Javaserver status to id: " + originid);

				this.sendServerStatusToTargetId(originid);

			} else {
				log.debug("JAVASERVER: Redirecting STATUSREQUEST-message to id: "
						+ targetid);
				// push to target
				this.sendMessage(message, targetid);
				this.setLastMessageSent(message);
			}
		}
		// process network message
		else if (message.getType() == DSMessageType.NETWORK) {
			log.debug("JAVASERVER: Network messages aren't implemented yet!");
		}

	}
	
	public static void main(String[] args) {
		Server s = new Server();
		
		log.debug("Starting Javaserver!");
		try {
			s.connect();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		while(true) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
